﻿using System;
using System.Collections.Generic;
using System.IO;
using FBGraph.Async;
using System.Collections.Specialized;
using System.Net;

namespace FBGraph
{
    /// <summary>Specifies a contract for providing access to all Graph API functionality.</summary>
    public interface IFBGraphContext
    {
        /// <summary>Gets the <see cref="AccessToken" /> for the current user.</summary>
        AccessToken? AccessToken { get; }

        /// <summary>Gets a <see cref="Boolean" /> value representing whether the current user has a valid <see cref="AccessToken" /> for your application.</summary>
        Boolean IsAuthorized { get; }

        /// <summary>Gets the FBGraph User ID of the current user if they have a valid <see cref="AccessToken" />.</summary>
        Int64? UserID { get; }

        /// <summary>Gets or sets the set of extended permissions the current user had granted the application.</summary>
        ExtendedPermission ExtendedPermissions { get; }

        /// <summary>Gets the Facebook application's Client ID</summary>
        Int64 ClientID { get; }

        /// <summary>Gets the Facebook application's Client Secret</summary>
        String ClientSecret { get; }

        /// <summary>Gets a reference to a <see cref="FBGraph.Configuration.FBGraphConfigurationManager" /> for reading data from the configuration.</summary>
        Configuration.FBGraphConfigurationManager Config { get; }

        /// <summary>Generates a Graph API uri for the given <paramref name="connection" />, <paramref name="queryStringValues" /> and <paramref name="ids" />.</summary>
        /// <param name="connection">The optional name of a Graph API connection to query against.</param>
        /// <param name="qs">An optional collection of name/value pairs to put on the querystring of the resulting uri, often used for filtering results.</param>
        /// <param name="ids">One or more Graph API object ids to return data for.</param>
        /// <returns>A <see cref="Uri" /> that can be used to request data from the Graph API for the specified parameters.</returns>
        /// <remarks>If <paramref name="connection" /> is specified, must be only one object specified for <paramref name="ids" />.</remarks>
        Uri CreateRequestUri(String connection, NameValueCollection qs, params Object[] ids);

        /// <summary>Executes the specified <see cref="HttpWebRequest" />.</summary>
        /// <typeparam name="TResult">The type of object that is returned.</typeparam>
        /// <param name="request">The <see cref="HttpWebRequest" /> to execute.</param>
        /// <returns>A <typeparamref name="TResult" /> object containing the result of the request.</returns>
        TResult Execute<TResult>(FBGraphApiRequest request)
            where TResult : class, IFBGraphResult, new();
        
        /// <summary>Gets the Graph object with the specified <paramref name="id" />.</summary>
        /// <typeparam name="TModel">The type of object that should be returned.</typeparam>
        /// <param name="id">The Graph identifier or alias of the object that should be retrieved.</param>
        /// <param name="fields">An optional array of field names to select instead of the default set of fields for the query.</param>
        /// <returns>A <typeparamref name="TModel" /> object that has been deserialized from the JSON response of the GET request.</returns>
        /// <exception cref="ArgumentNullException"><paramref name="id" /> was null.</exception>
        /// <exception cref="ArgumentException"><paramref name="id" /> was an empty string.</exception>
        TModel GetSingle<TModel>(Object id, String[] fields)
            where TModel : class, IFBGraphResult, new();

        /// <summary>Gets the Graph objects with the specified <paramref name="ids" />.</summary>
        /// <typeparam name="TModel">The type of object the identifiers in <paramref name="ids" /> reference.</typeparam>
        /// <param name="fields">An optional array of field names to select instead of the default set of fields for the query.</param>
        /// <param name="ids">One or more Graph identifiers or aliases of the objects that should be retrieved.</param>
        /// <returns>A <see cref="DictionaryResult{TModel}" /> of <typeparamref name="TModel" /> objects keyed by their identifiers that have been deserialized from the JSON response of the GET request.</returns>        
        /// <exception cref="ArgumentNullException"><paramref name="ids" /> was null.</exception>
        /// <exception cref="ArgumentException"><paramref name="ids" /> contained a null value or empty string.</exception>
        DictionaryResult<TModel> GetMany<TModel>(String[] fields, params Object[] ids);

        /// <summary>Executes a basic HTTP GET operation and deserializes the result into an instance of <typeparamref name="TResult"/>.</summary>
        /// <typeparam name="TResult">The type of object that will be returned.</typeparam>
        /// <param name="uri">The url of the query to get.</param>
        /// <returns>An instance of <typeparamref name="TModel" /> containing the result of the query deserialized from JSON.</returns>
        /// <remarks>
        /// <typeparamref name="TResult" /> should account for any collection classes required to contain the resulting objects, such as
        /// an array or <see cref="PagedResult{TModel}" />.
        /// </remarks>
        TResult Get<TResult>(Uri uri)
            where TResult : class, IFBGraphResult, new();

        /// <summary>Gets a collection of objects associated with the specified connection for the object identified by <paramref name="id" />.</summary>
        /// <param name="id">The identifier of the object whose connection is being queried.</param>
        /// <param name="connection">The name of the connection from which to retrieve objects.</param>
        /// <param name="fields">An optional array of field names to select instead of the default set of fields for the query.</param>
        /// <param name="since">The minimum post date/time to filter results by.</param>
        /// <param name="until">The maximum post date/time to filter results by.</param>
        /// <param name="limit">The maximum number of objects to return.</param>
        /// <param name="offset">The index offset from which to start returning results.</param>
        /// <returns>A collection of objects associated with the connection being queried.</returns>
        PagedResult<TModel> GetConnection<TModel>(Object id, String connection, String[] fields, DateTime? since, DateTime? until, Int32? limit, Int32? offset);
        
        /// <summary>Performs a Graph API search for the given query and constraining parameters.</summary>
        /// <typeparam name="TModel">The type of object to return.</typeparam>
        /// <param name="query">The search term or keywords.</param>
        /// <param name="type">The type of search to perform.</param>
        /// <param name="fields">An optional array of field names to select instead of the default set of fields for the query.</param>
        /// <param name="since">The minimum post date/time to filter results by.</param>
        /// <param name="until">The maximum post date/time to filter results by.</param>
        /// <param name="limit">The maximum number of objects to return.</param>
        /// <param name="offset">The index offset from which to start returning results.</param>
        /// <returns>A <see cref="PagedResult{TModel}" /> containing the results of the search query.</returns>
        PagedResult<TModel> Search<TModel>(String query, String type, String[] fields, DateTime? since, DateTime? until, Int32? limit, Int32? offset);

        /// <summary>Creates an object for the specified connection by executing an HTTP POST operation with the specified arguments.</summary>
        /// <param name="id">The target object id for which the connection object is being created.</param>
        /// <param name="connection">The name of the connection where the object is being created.</param>
        /// <param name="arguments">A <see cref="IDictionary{String, Object} " /> of arguments required by the Graph API to complete the operation.</param>
        /// <returns>A <see cref="String" /> or numeric value containing the object id of the object that was created.</returns>
        IdResult Post(Object id, String connection, IDictionary<String, Object> arguments);

        /// <summary>Creates an object for the specified connection by uploading binary data using an HTTP POST operation with the specified arguments.</summary>
        /// <param name="id">The target object id for which the connection object is being created.</param>
        /// <param name="connection">The name of the connection where the object is being created.</param>
        /// <param name="arguments">A <see cref="IDictionary{String, Object} " /> of arguments required by the Graph API to complete the operation.</param>
        /// <param name="filename">The filename of the file being uploaded.</param>
        /// <param name="fileparameter">The name of the parameter used to identify the file.</param>
        /// <param name="contentType">The MIME content type of the file being uploaded.</param>
        /// <param name="data">A <see cref="Stream" /> object containing the file that will be uploaded.</param>
        /// <returns>A <see cref="String" /> or numeric identifier of the object that was created.</returns>
        IdResult PostFile(Object id, String connection, IDictionary<String, Object> arguments, String filename, String fileparameter, String contentType, Stream data);

        /// <summary>Deletes a Graph API object.</summary>
        /// <param name="id">The identifier of the object that is being deleted.</param>
        /// <param name="connection">The optional name of the connection where the object is being deleted. This is
        /// currently only used for removing likes from a post.</param>
        /// <returns>The <see cref="String" /> or numeric identifier of the object that was deleted.</returns>
        IdResult Delete(Object id, String connection);

        /// <summary>Starts asynchronous execution and queueing of Graph API requests.</summary>
        /// <returns>A <see cref="AsyncContainer" /> instance that will contain a queue of Graph API requests and a collection of any
        /// errors that occur during the course of their execution.</returns>
        AsyncContainer StartAsync();

        /// <summary>Blocks the current thread until all asynchronous requests have been completed.</summary>
        void CompleteAsync();

        /// <summary>Begins an asynchronous operation that waits for any queued requests to complete.</summary>
        /// <param name="callback">The <see cref="AsyncCallback" /> delegate.</param>
        /// <param name="state">The state object for the request.</param>
        /// <returns>An <see cref="IAsyncResult" /> that references the asynchronous completion request.</returns>
        IAsyncResult BeginCompleteAsync(AsyncCallback callback, Object state);

        /// <summary>Ends an asychronous completion operation.</summary>
        /// <param name="result">The pending request to complete queued requests.</param>
        void EndCompleteAsync(IAsyncResult result);
    }
}